#include "controlBot.h"
#include <iostream>
#include <windows.h>
#include <cmath> 
#include <math.h>

//////////////////////////////////////////////////////////////////////////
//Author	:	Ross Conroy	ross.conroy@tees.ac.uk
//Date		:	06/05/2014
//
//This class controls a single bot/agent, each time step it makes an
//observation of whether the enemy is visible and then takes actions
//by expanding an influence diagram into a policy tree to determine what
//action the agent should take based on the current observation and
//time step.
//
//Update
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	13/05/2014
//Updated to use new decision tree/policy parser
//
//Update
//Author	:	Ross Conroy ross.cornoy@tees.ac.uk
//Date		:	22/05/2014
//Updated escape to determine direction away from enemy to escape to
//////////////////////////////////////////////////////////////////////////

ControlBot::ControlBot()
{
	_agent = *Broodwar->self()->getUnits().begin();
	//_agent = * Broodwar->enemy()->getUnits().begin();
	_currentStep = 0;
	_timeSinceLastStep = 0;
	_timeBetweenSteps = 750; //0.75 second between decisions
	
	_decisionTreeParser = new PolicyFileParser("aj", "oiSeeEnemy", "C:\\Nets\\Policies\\Mod0\\" , ".txt", true);
	_maxSteps = _decisionTreeParser->GetNumberOfFilesInFolder("C:\\Nets\\Policies\\Mod0\\", ".txt");

	_escapeDistance = 50;
	//_lastKnownEnemyPosition;
}

//Gets the time difference between each tick of the game clock
float diffclock(clock_t clock1, clock_t clock2)
{
	float diffticks=clock2-clock1;
	float diffms=(diffticks*1000)/CLOCKS_PER_SEC;
	return diffms;
}

//Tick, each clock cycle in game
void ControlBot::Tick()
{
	float deltaTime = diffclock(_lastTime, clock());
	_lastTime = clock();
	_timeSinceLastStep += deltaTime;

	if(_timeSinceLastStep >= _timeBetweenSteps)
	{
		_timeSinceLastStep = 0;
		MakeDecision();
		_currentStep++;

		if(_currentStep >= _maxSteps)
		{
			_currentStep = 0;
		}
	}
}

//Used the hugin API to determine if the agent should attack or escape
void ControlBot::MakeDecision()
{
	string action;

	if(CanSeeEnemy())
	{
		action = _decisionTreeParser->DecisionFromObservation(_currentStep, "See");
	}
	else
	{
		action = _decisionTreeParser->DecisionFromObservation(_currentStep, "DontSee");
	}

	if(action == "Attack")
	{
		Attack();
	}
	else if(action == "Escape")
	{
		Escape();
	}
	else
	{
		Broodwar->printf("Invalid Decision ");
	}
}

//actions to attack
//attacking is simply finding the nearest enemy then attacking it
//ToDo fix why agent not attacking enemy
void ControlBot::Attack()
{
	try
	{
		Broodwar->printf("Attack");

		Unit* closestEnemy=NULL;
		for(std::set<Unit*>::const_iterator m = Broodwar->enemy()->getUnits().begin(); m!=Broodwar->enemy()->getUnits().end(); m++)
		{
			if (closestEnemy==NULL || (_agent)->getDistance(*m)<(_agent)->getDistance(closestEnemy))
				closestEnemy=*m;
		}

		if(closestEnemy != NULL)
		{
			_agent->attack(closestEnemy, false);
			//_agent->attack(closestEnemy->getPosition(), false);
		}
		else
		{
			if(_lastKnownEnemyPosition != NULL)
			{
				_agent->move(_lastKnownEnemyPosition);
			}
			Broodwar->printf("No Enemy Available");
		}
	}
	catch(exception e)
	{
		cout << e.what() << endl;
	}	
}

//Actions to escape
//Escaping is simply moving away from the enemy agent
void ControlBot::Escape()
{
	try
	{
		Broodwar->printf("Escape");

		Unit* closestEnemy=NULL;
		for(std::set<Unit*>::const_iterator m = Broodwar->enemy()->getUnits().begin(); m!=Broodwar->enemy()->getUnits().end(); m++)
		{
			if (closestEnemy==NULL || (_agent)->getDistance(*m)<(_agent)->getDistance(closestEnemy))
				closestEnemy=*m;
		}

		if(closestEnemy != NULL)
		{
			Position enemyPos = closestEnemy->getPosition();
			Position unitPos = _agent->getPosition();
			int diffX = enemyPos.x() - unitPos.x();
			int diffY = enemyPos.y() - unitPos.y();
			float tmp = (diffX*diffX) + (diffY*diffY);
			float dist = sqrt(tmp);
			float multi = _escapeDistance / dist;

			diffX = diffX * -1 * multi;
			diffY = diffY * -1 * multi;

			Position newPos;
			newPos.x() = unitPos.x() + diffX;
			newPos.y() = unitPos.y() + diffY;

			//pos.x() = pos.x() + 100;
			//pos.y() = pos.y() + 100;
			_agent->move(newPos);
		}
	}
	catch(exception e)
	{
		cout << e.what() << endl;
	}
}

//Determines whether an enemy is visible
bool ControlBot::CanSeeEnemy()
{	
	try
	{
		Unit* closestEnemy=NULL;
		for(std::set<Unit*>::const_iterator m = Broodwar->enemy()->getUnits().begin(); m!=Broodwar->enemy()->getUnits().end(); m++)
		{
			if (closestEnemy==NULL || (_agent)->getDistance(*m)<(_agent)->getDistance(closestEnemy))
				closestEnemy=*m;
		}

		//Unit * enemy = *Broodwar->enemy()->getUnits().begin();

		if(closestEnemy != NULL)
		{				
			if(closestEnemy->isVisible())
			{
				Broodwar->printf("Enemy Visible");
				_lastKnownEnemyPosition = closestEnemy->getPosition();
			}
			else
			{
				Broodwar->printf("Enemy Invisible");
			}
			return closestEnemy->isVisible();
		}
		else
		{
			Broodwar->printf("No Enemies");
			return false;
		}
	}
	catch(exception e)
	{
		cout << e.what() << endl;
		return false;
	}	
}
